package com.example.mybatis.aspect;


import com.example.mybatis.controller.ExcelController;
import com.example.mybatis.controller.MybatisController;
import com.example.mybatis.form.UserForm;
import com.example.mybatis.model.LogInfoTO;
import com.example.mybatis.model.ServiceResponse;
import com.example.mybatis.service.IApiLogService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

@Component
@Aspect
@Order(value = 2)
public class LogRecordAspect {

    private final static Logger logger = LoggerFactory.getLogger(LogRecordAspect.class);

    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Value("${server.servlet.context-path}")
    private String contextPath;

    @Autowired
    private IApiLogService apiLogService;
    /**
     *切点：所有控制器的Rest接口方法
     */
    @Pointcut("within(com.example.mybatis.controller.*Controller) && @annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void pointCut(){

    }

    @Around("pointCut()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint){
        Object result = null;
        Signature signature = joinPoint.getSignature();
        Class clazz = signature.getDeclaringType();
        RequestMapping requestMapping1 = (RequestMapping) clazz.getDeclaredAnnotation(RequestMapping.class);
        String path1 = requestMapping1.value()[0];
        if(!StringUtils.isEmpty(path1)){
            if(!path1.startsWith("/")){
                path1 = "/"+path1;
            }
        }
        String signatureName = signature.getName();
        logger.info("signatureName={}",signatureName);
        //获取擦数类型
        Object[] args = joinPoint.getArgs();
        Class[] paramTypes = new Class[args.length];
        if(clazz== MybatisController.class){
            if(signatureName.equals("userLogin")){
                paramTypes[0] = UserForm.class;
                paramTypes[1] = HttpServletRequest.class;
                logger.info("args[1] className={}",args[1].getClass().getName());
            }else if(signatureName.equals("loginOut")){
                paramTypes[0] = HttpServletRequest.class;
            }else{
                for(int i=0;i<args.length;i++){
                    paramTypes[i] = args[i].getClass();
                }
            }
        }else if(clazz== ExcelController.class){
            if(signatureName.equals("exportExcel")){
                paramTypes[0] = String.class;
                paramTypes[1] = HttpServletResponse.class;
            }else if(signatureName.equals("exportSearchExcel")){
                paramTypes[0] = UserForm.class;
                paramTypes[1] = HttpServletResponse.class;
            }else if(signatureName.equals("importExcel")){
                paramTypes[0] = MultipartFile.class;
                paramTypes[1] = HttpServletRequest.class;
            }
        }else{
            for(int i=0;i<args.length;i++){
                paramTypes[i] = args[i].getClass();
            }
        }

        //获取接口请求类型和请求URL
        try {
            Method method = clazz.getDeclaredMethod(signatureName,paramTypes);
            RequestMapping requestMapping2 =  method.getDeclaredAnnotation(RequestMapping.class);
            String path2 = requestMapping2.value()[0];
            if(!path2.startsWith("/")){
                path2 = "/"+path2;
            }
            String restType = "";
            RequestMethod[] requestTypes = requestMapping2.method();
            if(requestTypes.length==0){
                restType = "GET";
            }else{
                restType = requestTypes[0].toString();
            }
            String restUrl = new StringBuilder(contextPath).append(path1).append(path2).toString();
            LogInfoTO logInfoTO = new LogInfoTO();
            logInfoTO.setLogId(UUID.randomUUID().toString().replace("-",""));
            logInfoTO.setRestType(restType);
            logInfoTO.setRestUrl(restUrl);
            long invokeStartTime = System.currentTimeMillis();
            String startTime = sdf.format(new Date());
            logInfoTO.setStartTime(startTime);
            int resultFlag;
            long invokeEndTime;
            ServiceResponse<String> exceptionResponse = new ServiceResponse<>();
            //执行接口方法逻辑
            try{
                if(args.length==0){
                    result = joinPoint.proceed();
                }else{
                    result = joinPoint.proceed(args);
                }
                invokeEndTime = System.currentTimeMillis();
                resultFlag = 1;
            }catch (Throwable ex){
                invokeEndTime = System.currentTimeMillis();
                resultFlag = 0;
                logger.error("invoke signature method error",ex);
                exceptionResponse.setStatus(500);
                exceptionResponse.setMessage("Inner Server Error,Caused by: "+ex.getMessage());

            }
            long expenseTime = invokeEndTime - invokeStartTime;
            logInfoTO.setResultFlag(resultFlag);
            logInfoTO.setExpenseTime(expenseTime);
            try {
                apiLogService.addApiLog(logInfoTO);
            } catch (Exception ex2) {
                logger.error("add apiLog failed",ex2);
            }
            if(resultFlag==0){ //如果调用接口逻辑方法发生异常，则返回异常对象
                return exceptionResponse;
            }
        } catch (NoSuchMethodException e) {
           logger.error("",e);
        }
        return result;

    }

}
